﻿/*******************************************************************************

    Units of Measure for C# applications

    Copyright (C) 2013 Marek Aniola

    This program is provided to you under the terms of the license
    as published at http://unitsofmeasure.codeplex.com/license


********************************************************************************/
using System;
using System.Globalization;

namespace Man.UnitsOfMeasure
{
	public class TextParser<T> : ITextParser<T> where T : struct
	{
		#region Statics

		private static ITypeHost<T> TypeHost = Measure<T>.InitializeTypeHost();

		#endregion Statics

		#region Methods

		public bool TryParse(string input, UnitSystem<T> units, NumberStyles style, IFormatProvider fp, out Quantity<T> result)
		{
			result = null;
			int index = FindUnitSymbolIndex(input, fp);
			if ((0 <= index) && (index < input.Length))
			{
				UnitOfMeasure<T> unit = units[input.Substring(index).TrimStart()];
				T value;
				if ((unit != null) && TypeHost.TryParse(input.Substring(0, index), style, fp, out value))
				{
					result = new Quantity<T>(value, unit);
				}
			}
			return (result != null);
		}

		public bool TryParse(string input, UnitSystem<T> units, NumberStyles style, IFormatProvider fp, out Level<T> result)
		{
			result = null;
			int index = FindUnitSymbolIndex(input, fp);
			if ((0 <= index) && (index < input.Length))
			{
				UnitOfMeasure<T> unit = units[input.Substring(index).TrimStart()];
				T value;
				if ((unit != null) && TypeHost.TryParse(input.Substring(0, index), style, fp, out value))
				{
					result = new Level<T>(value, unit);
				}
			}
			return (result != null);
		}

		private int FindUnitSymbolIndex(string input, IFormatProvider fp)
		{
			NumberFormatInfo nfi = fp.GetFormat(typeof(NumberFormatInfo)) as NumberFormatInfo;
			if (nfi == null) throw new ArgumentException(String.Format("{0}.{1}: no NumberFormatInfo data required for parsing.", this.GetType().Namespace, this.GetType().Name));

			if (input.StartsWith(nfi.NaNSymbol, StringComparison.OrdinalIgnoreCase)) return nfi.NaNSymbol.Length;
			if (input.StartsWith(nfi.PositiveInfinitySymbol, StringComparison.OrdinalIgnoreCase)) return nfi.PositiveInfinitySymbol.Length;
			if (input.StartsWith(nfi.NegativeInfinitySymbol, StringComparison.OrdinalIgnoreCase)) return nfi.NegativeInfinitySymbol.Length;

			int index = 0;
			while (index < input.Length)
			{
				int digitIndex = Array.FindIndex(nfi.NativeDigits, digit => String.CompareOrdinal(input, index, digit, 0, digit.Length) == 0);
				if (digitIndex >= 0)
					index += nfi.NativeDigits[digitIndex].Length;
				else if (String.CompareOrdinal(input, index, nfi.NumberDecimalSeparator, 0, nfi.NumberDecimalSeparator.Length) == 0)
					index += nfi.NumberDecimalSeparator.Length;
				else if (String.CompareOrdinal(input, index, nfi.NumberGroupSeparator, 0, nfi.NumberGroupSeparator.Length) == 0)
					index += nfi.NumberGroupSeparator.Length;
				else if (String.CompareOrdinal(input, index, nfi.NegativeSign, 0, nfi.NegativeSign.Length) == 0)
					index += nfi.NegativeSign.Length;
				else if (String.CompareOrdinal(input, index, nfi.PositiveSign, 0, nfi.PositiveSign.Length) == 0)
					index += nfi.PositiveSign.Length;
				else
					break;
			}
			return index;
		}

		#endregion Methods
	}
}
